<template>
  <div class="designer">
    <div class="designer-header">
      <div class="back" @click="goBack">
        <el-tooltip effect="dark" content="返回" placement="top">
          <el-icon :size="22" color="#fff"><Back /></el-icon>
        </el-tooltip>
      </div>
      <div class="title">大屏设计器</div>
      <div class="operation">
        <el-button @click="viewDesign" :loading="isPreviewSubmit">预览</el-button>
        <el-button @click="save" type="primary" :loading="isSubmit">保存</el-button>
      </div>
    </div>
    <div class="designer-body">
      <div class="designer-left">
        <el-tabs class="left-tab" tab-position="left">
          <el-tab-pane v-for="(item, index) in widgetTools" :key="index" :label="item.name">
            <li
              v-for="(it, idx) in item.list"
              :key="idx"
              draggable="true"
              @dragstart="dragStart(it.code)"
              @dragend="dragEnd()"
            >
              <div class="tools-item">
                <img :src="widgetImgMapper[it.code]" />
                <div class="tools-item-text">{{ it.label }}</div>
              </div>
            </li>
          </el-tab-pane>
        </el-tabs>
      </div>
      <div class="designer-content">
        <div class="top-button">
          <span
            :class="{
              btn: true,
              'btn-disable': currentSizeRangeIndex === 0
            }"
            @click="setSize(0)"
          >
            <el-tooltip
              class="item"
              :disabled="currentSizeRangeIndex === 0"
              effect="dark"
              content="缩小"
              placement="bottom"
            >
              <el-icon><Minus /></el-icon>
            </el-tooltip>
          </span>
          <span
            :class="{
              btn: true,
              'scale-num': true,
              'btn-disable': currentSizeRangeIndex === defaultSize.index
            }"
            @click="setSize(2)"
          >
            <el-tooltip
              class="item"
              :disabled="currentSizeRangeIndex === defaultSize.index"
              effect="dark"
              content="默认比例"
              placement="bottom"
            >
              <span> {{ parseInt('' + scaleNum) }}% </span>
              {{ currentSizeRangeIndex }}
            </el-tooltip>
          </span>
          <span
            :class="{
              btn: true,
              'btn-disable': currentSizeRangeIndex === 8
            }"
            @click="setSize(1)"
          >
            <el-tooltip
              class="item"
              :disabled="currentSizeRangeIndex === 8"
              effect="dark"
              content="放大"
              placement="bottom"
            >
              <el-icon><Plus /></el-icon>
            </el-tooltip>
          </span>
        </div>
        <div class="workbench-container" @click.self="setOptionsOnClickScreen">
          <div
            v-if="isLoad"
            class="vue-ruler-tool-wrap"
            :style="{
              width: +bigscreenWidth + 18 + 'px',
              height: +bigscreenHeight + 18 + 'px',
              transform:
                currentSizeRangeIndex === defaultSize.index
                  ? workbenchTransform
                  : `scale(${sizeRange[currentSizeRangeIndex] / 100})`,
              transformOrigin: '0 0'
            }"
          >
            <vue3-ruler-tool
              ref="rulerToolRef"
              :value="dashboard.presetLine"
              :step-length="50"
              :parent="true"
              :position="'relative'"
              :is-scale-revise="true"
              @input="handleRulerInput"
            >
              <div
                id="workbench"
                class="workbench"
                ref="workbenchRef"
                :style="{
                  width: bigscreenWidth + 'px',
                  height: bigscreenHeight + 'px',
                  'background-color': dashboard.options?.setup.backgroundColor,
                  'background-image': 'url(' + dashboard.options?.setup.backgroundImage + ')',
                  'background-position': '0% 0%',
                  'background-size': '100% 100%',
                  'background-repeat': 'initial',
                  'background-attachment': 'initial',
                  'background-origin': 'initial',
                  'background-clip': 'initial'
                }"
                @click.self="setOptionsOnClickScreen"
                @drop="widgetOnDragged($event)"
                @dragover="dragOver($event)"
              >
                <widget
                  ref="widgetRefs"
                  v-for="(widgetItem, index) in widgets"
                  :key="index"
                  :widgetData="widgetItem"
                  :index="index"
                  :step="step"
                  @on-activated="setOptionsOnClickWidget"
                  @mousedown.prevent="widgetsClick($event, index)"
                  @contextmenu.prevent="rightClick($event, index)"
                />
              </div>
            </vue3-ruler-tool>
          </div>
        </div>
      </div>

      <div class="designer-right">
        <dynamicForm
          v-if="rightWidget"
          :currentWidget="rightWidget"
          @on-changed="widgetValueChanged"
        />
      </div>
    </div>

    <content-menu
      v-model:visible="visibleContentMenu"
      :style-obj="styleObj"
      @deletelayer="deletelayer"
      @lock-layer="lockLayer"
      @no-lock-layer="noLockLayer"
      @istop-layer="istopLayer"
      @setlow-layer="setlowLayer"
      @moveup-layer="moveupLayer"
      @movedown-layer="movedownLayer"
    />
  </div>
</template>
<script lang="ts" setup>
import { ref, nextTick, onMounted } from 'vue'
import Vue3RulerTool from './components/vue3RulerTool/index.vue'
import widget from './widget/index.vue'
import dynamicForm from './dynamicForm/index.vue'
import contentMenu from './components/contentMenu.vue'
import { widgetTools, getToolByCode } from './tools/index'
import { Minus, Plus, Back } from '@element-plus/icons-vue'
import _ from 'lodash'
import { useScreenSize } from './hooks/useSceenSize'
import { useTools } from './hooks/useTools'
import { useContentMenu } from './hooks/useContentMenu'
import { widgetImgMapper } from './tools/widgetImgMapper'
import { useRouter } from 'vue-router'
import * as FileApi from '@/api/infra/file'
import { toJpeg } from 'html-to-image'
import { BigscreenApi } from '@/api/vmiot/bigscreen'

const { query } = useRoute()
const router = useRouter()
const { push } = router
const id = ref<number>(Number(query.id))
const rulerToolRef = ref<any>(null)
const dashboard = ref<any>({})
const dragWidgetCode = ref('')

const widgets = ref<any[]>([])
const widgetRefs = ref<any[]>([])
const widgetIndex = ref(0)
const workbenchRef = ref<HTMLElement | null>(null)

const currentWidgetTotal = ref(0)

// 用于右侧表单
const rightWidget = ref<any>()

const isLoad = ref(false)

const {
  currentSizeRangeIndex,
  sizeRange,
  bigscreenScaleInWorkbench,
  workbenchTransform,
  bigscreenWidth,
  bigscreenHeight,
  defaultSize,
  scaleNum,
  setSize,
  step
} = useScreenSize()

const { getDomTopLeftById } = useTools()

const {
  visibleContentMenu,
  styleObj,
  rightClick,
  deletelayer,
  lockLayer,
  noLockLayer,
  istopLayer,
  setlowLayer,
  moveupLayer,
  movedownLayer
} = useContentMenu(widgets, widgetIndex)

const dragStart = (widgetCode) => {
  dragWidgetCode.value = widgetCode
  currentWidgetTotal.value = widgets.value.length
}

const dragEnd = async () => {
  await nextTick(() => {
    if (widgets.value.length === currentWidgetTotal.value + 1) {
      // 新增了一个组件
      const newWidget = widgets.value[currentWidgetTotal.value]
      const uuid = Number(Math.random().toString().substr(2)).toString(36)
      newWidget.widgetId = uuid
      const index = widgets.value.length - 1
      widgetIndex.value = index
    }
  })
}

const widgetOnDragged = (evt) => {
  const widgetType = dragWidgetCode.value
  // 获取结束坐标和列名
  const eventX = evt.clientX // 结束在屏幕的x坐标
  const eventY = evt.clientY // 结束在屏幕的y坐标
  const workbenchPosition = getDomTopLeftById('workbench')
  const widgetTopInWorkbench = eventY - workbenchPosition.top
  const widgetLeftInWorkbench = eventX - workbenchPosition.left
  const targetScale: any =
    currentSizeRangeIndex.value === defaultSize.value.index
      ? bigscreenScaleInWorkbench.value
      : sizeRange[currentSizeRangeIndex.value] / 100
  const x: any = widgetLeftInWorkbench / targetScale
  const y: any = widgetTopInWorkbench / targetScale
  // 复制一个组件
  const widgetJsonValue: any = getToolByCode(widgetType)
  widgetJsonValue.options.position.left = x - widgetJsonValue.options.position.width / 2
  widgetJsonValue.options.position.top = y - widgetJsonValue.options.position.height / 2
  // 将选中的复制组件，放到工作区中去
  widgets.value.push(_.cloneDeep(widgetJsonValue))
}

const dragOver = (evt) => {
  evt.preventDefault()
  evt.stopPropagation()
  evt.dataTransfer.dropEffect = 'copy'
}

/**
 * 点击大屏底层
 */
const setOptionsOnClickScreen = () => {
  clearWidgetsSelected()
  rightWidget.value = dashboard.value
}

// 如果是点击某个组件，获取该组件的配置项
const setOptionsOnClickWidget = (obj) => {
  widgets.value[obj.index].options.position = obj
}

const clearWidgetsSelected = () => {
  widgetRefs.value.forEach((item, index) => {
    widgetRefs.value[index].$refs.draggable?.setActive(false)
  })
}

const widgetsClick = (event, index) => {
  clearWidgetsSelected()
  widgetRefs.value[index].$refs.draggable?.setActive(true)
  rightWidget.value = widgets.value[index]
}

const widgetValueChanged = async (e, type, key, val) => {
  if (e.code === 'screen' && type === 'setup' && (key === 'width' || key === 'height')) {
    bigscreenWidth.value = e.options.setup.width
    bigscreenHeight.value = e.options.setup.height
    if (rulerToolRef.value) {
      await nextTick()
      rulerToolRef.value.init()
    }
  }
}

/**
 * 新增大屏，初始化数据
 */
const initScreenData = () => {
  dashboard.value = getToolByCode('screen')
  setOptionsOnClickScreen()
  isLoad.value = true
}

/**
 * 大屏处理数据
 * @param data
 */
const resolveScreenData = (data) => {
  console.log(data)
  dashboard.value = data
  widgets.value = data.widgets
  bigscreenWidth.value = data.options.setup.width
  bigscreenHeight.value = data.options.setup.height
  setOptionsOnClickScreen()
  isLoad.value = true
}

const getData = async () => {
  const res = await BigscreenApi.getBigscreen(id.value)

  const dashboardData = JSON.parse(res.content)
  if (!Array.isArray(dashboardData.presetLine)) {
    dashboardData.presetLine = []
  }
  resolveScreenData(dashboardData)
}

const handleRulerInput = (value: any) => {
  dashboard.value.presetLine = value
}

// 上传图片函数（接收 blob）
const uploadImage = async (blob: Blob): Promise<string> => {
  const formData = new FormData()
  formData.append('file', blob, `screenshot.png`)
  return new Promise((resolve, reject) => {
    FileApi.updateFile(formData)
      .then((res) => {
        if (res.code === 0) {
          resolve(res)
        } else {
          reject(res)
        }
      })
      .catch((res) => {
        reject(res)
      })
  })
}

// 截图并上传，返回图片 URL
const captureAndUpload = async (): Promise<string> => {
  if (!workbenchRef.value) {
    throw new Error('未找到工作区元素')
  }

  try {
    // 1. 生成图片
    const dataUrl = await toJpeg(workbenchRef.value, {
      skipFonts: false
    })

    // 2. 转为 Blob
    const res = await fetch(dataUrl)
    const blob = await res.blob()

    // 3. 上传图片，返回 URL
    const result: any = await uploadImage(blob)
    return result.data
  } catch (error) {
    console.error('截图失败:', error)
    throw error
  }
}

/**
 * 返回
 */
const goBack = () => {
  push('/scene/bigscreen')
}

const isPreviewSubmit = ref(false)
/**
 * 预览
 */
const viewDesign = () => {
  if (isPreviewSubmit.value) {
    return
  }
  isPreviewSubmit.value = true
  saveSubmit((newId) => {
    const lastId = id.value ? id.value : newId
    const routeUrl = router.resolve({
      path: '/bigscreen/viewer',
      query: { id: lastId }
    })
    isPreviewSubmit.value = false
    id.value = lastId
    refreshData()
    push('/bigscreen/designer?id=' + lastId)
    window.open(routeUrl.href, '_blank')
  }, 'preview')
}

const isSubmit = ref(false)

/**
 * 保存
 */
const save = async () => {
  clearWidgetsSelected()
  const screenName = dashboard.value.options.setup.name
  if (!screenName) {
    ElMessage.error('请填写大屏名称')
    setOptionsOnClickScreen()
    return
  }

  if (isSubmit.value) return
  isSubmit.value = true
  await saveSubmit(() => {
    if (id.value) {
      ElMessage.success('更新成功')
    } else {
      ElMessage.success('保存成功')
    }
    isSubmit.value = false
    push('/scene/bigscreen')
  }, 'save')
}

/**
 * 提交大屏数据
 * @param callback
 */
const saveSubmit = async (callback?: (id?: any) => void, type: 'preview' | 'save' = 'save') => {
  try {
    const imageUrl = await captureAndUpload()

    const dashboardContent = {
      ...dashboard.value,
      widgets: [...widgets.value]
    }

    const params: any = {
      id: id.value,
      name: dashboardContent.options.setup.name,
      cover: imageUrl,
      remark: dashboardContent.options.setup.description,
      content: JSON.stringify(dashboardContent)
    }

    let resultId
    if (params.id) {
      await BigscreenApi.updateBigscreen(params)
    } else {
      const res = await BigscreenApi.createBigscreen(params)
      resultId = res
    }

    if (callback) callback(resultId)
  } catch (e) {
    console.error(e)
  } finally {
    // 确保还原提交状态
    if (type === 'preview') {
      isPreviewSubmit.value = false
    } else {
      isSubmit.value = false
    }
  }
}

const refreshData = () => {
  isLoad.value = false
  if (id.value) {
    getData()
  } else {
    initScreenData()
  }
}

onMounted(async () => {
  refreshData()
})
</script>
<style lang="scss">
.designer {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: #202023;
}
.designer-header {
  height: 46px;
  display: flex;
  padding: 0 16px;
  border-bottom: 1px solid #e8e8e8;
  align-items: center;
  .back {
    cursor: pointer;
  }
  .title {
    flex: 1;
    color: #fff;
    margin-left: 10px;
  }
}

.designer-body {
  flex: 1;
  min-height: 0;
  display: flex;

  .top-button {
    display: flex;
    flex-direction: row;
    height: 40px;
    line-height: 40px;
    background-color: #212528;

    .btn {
      color: #788994;
      width: 55px;
      text-align: center;
      display: block;
      cursor: pointer;

      &:hover {
        background: rgb(25, 29, 34);
      }
    }

    .btn-disable {
      opacity: 0.3;
      cursor: no-drop;
    }

    .scale-num {
      color: #788994;
      opacity: 1;
      cursor: pointer;

      &.btn-disable {
        cursor: no-drop;

        &:hover {
          background: #20262c;
        }
      }
    }
  }
}

.designer-left {
  width: 200px;
  height: 100%;
  .tool-item {
    width: 110px;
    height: 80px;
    img {
      width: 100%;
      height: 100%;
    }
  }
}

.designer-right {
  width: 300px;
  height: 100%;
  overflow-y: auto;
}

.designer-content {
  flex: 1;
  min-width: 0;
  height: 100%;
}

.workbench-container {
  // background-color: rgb(52, 80, 106);
  background: url('@/assets/images/screen.png') repeat;
  position: relative;
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: auto;
  color: #fff;
}

.vue-ruler-tool-wrap {
  background-color: #042042;
}

.left-tab {
  width: 100%;
  height: 100%;
  .el-tabs__nav-wrap {
    &:after {
      background-color: transparent;
    }
  }

  .el-tabs__nav {
    .el-tabs__item {
      width: 68px;
      justify-content: center;
      color: #fff;
      padding: 0;
      font-size: 12px !important;

      &.is-active {
        background-color: #31455d;
      }
    }
  }

  .el-tabs__content {
    height: 100%;
    overflow-y: auto;
    li {
      list-style: none;
      padding-right: 10px;
    }
  }

  .tools-item {
    color: #fff;
    padding: 10px 0;
    font-size: 12px;
    cursor: pointer;
    img {
      width: 110px;
      height: 80px;
    }

    .tools-item-text {
      text-align: center;
    }
  }
}
</style>
